package neuronet.server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Vector;

import neuronet.common.NeuroNetCom;

import org.apache.log4j.Logger;

/** 
 * 
 * Thread for each connected clientOne instance of this thread will run for each client 
 * 
 * */
class TCPThread extends Thread {

    // the socket where to listen/write
    Socket socket;

    // Input- and OutputStreams
    ObjectInputStream inputStream;
    ObjectOutputStream outputStream;

    // log4j-Logger
    private static Logger logger = Logger.getLogger(TCPThread.class);

    TCPThread(Socket socket)
    {
	this.socket = socket;
    }

    /**
     * The working method of the class Thread
     */
    public void run()
    {
	/* Creating both Data Streams */
	//	logger.info("Thread trying to create Object IO-Streams");
	try
	{
	    // create output first
	    outputStream = new ObjectOutputStream(socket.getOutputStream());
	    outputStream.flush();
	    inputStream = new ObjectInputStream(socket.getInputStream());
	}
	catch (IOException e)
	{
	    logger.error("Exception while creating new IO-Streams: ", e);
	    NeuroNetServer.getGUI().addLogMessage("Exception while creating new IO-Streams");
	    return;
	}

	//	logger.info("Thread waiting for a string from the client");

	// read a String (which is an object)
	try
	{
	    String str = (String) inputStream.readObject();
	    String back = computeIncommingString(str);
	    outputStream.writeObject(back);
	    outputStream.flush();
	}
	catch (IOException e)
	{
	    logger.error("Exception while reading/writing streams: ", e);
	    NeuroNetServer.getGUI().addLogMessage("Exception while reading/writing streams");
	    return;
	}
	catch (ClassNotFoundException e)
	{
	    logger.error("Exception while reading/writing streams: ", e);
	    NeuroNetServer.getGUI().addLogMessage("Exception while reading/writing streams");
	    return;
	}

	// TODO: finally close socket (Destructor())
	//	// make a clean end
	//	finally
	//	{
	//	    try
	//	    {
	//		outputStream.close();
	//		inputStream.close();
	//	    }
	//	    catch (Exception e)
	//	    {
	//		logger.error("Exception while closing streams: ", e);
	//		return;
	//	    }
	//	}
    }

    private String computeIncommingString(String mess)
    {

	String type = NeuroNetCom.getParameterFormMessage("type", mess);

	String ret = "";
	if (type.equalsIgnoreCase("-1"))
	    return "status:error_no_type_definition_found!";
	else
	{
	    logger.debug(" type = -" + type + "-");
	    // TODO: DEBUG OPTION, deaktivate in release
	    if (type.equalsIgnoreCase("reset"))
	    {
		ret = handleResetMessage(mess);
	    }
	    if (type.equalsIgnoreCase("logon"))
	    {
		ret = handleLogonMessage(mess);
	    }
	    if (type.equalsIgnoreCase("logoff"))
	    {
		ret = handleLogoffMessage(mess);
	    }
	    if (type.equalsIgnoreCase("senddata"))
	    {
		ret = handleDataMessage(mess);
	    }
	    if (type.equalsIgnoreCase("config"))
	    {
		ret = handleConfigMessage(mess);
	    }
	    if (type.equalsIgnoreCase("getdata"))
	    {
		ret = handleGetMessages(mess);
	    }
	    if (type.equalsIgnoreCase("getclients"))
	    {
		ret = handleGetClients(mess);
	    }
	    if (type.equalsIgnoreCase("getinfo"))
	    {
		ret = handleGetInfo(mess);
	    }
	    if (type.equalsIgnoreCase("gettargets"))
	    {
		ret = handleGetTargets(mess);
	    }

	    return ret;
	}
    }

    private String handleGetInfo(String mess)
    {
	String clientID = NeuroNetCom.getParameterFormMessage("clientid", mess);
	String clientKey = NeuroNetCom.getParameterFormMessage("clientkey", mess);
	String requestClient = NeuroNetCom.getParameterFormMessage("requestclientid", mess);

	NeuroNetServer.getGUI().addLogMessage("Client " + clientID + " requests info about " + requestClient);

	if (NeuroNetServer.checkAuth(clientID, clientKey))
	{
	    ClientsTableItem client = NeuroNetServer.getClientItem(requestClient);

	    if (client != null)
	    {
		String retMess = "status:ok ";
		NeuroNetServer.getGUI().addLogMessage(" --" + retMess);

		retMess += " clientinfo:" + client.getClientInfo() + " ";
		retMess += " clientlast:" + client.getClientLast() + " ";
//		retMess += " clientip:" + client.getClientIP() + " ";
//		retMess += " clientport:" + client.getClientPort() + " ";
		retMess += " clienttargets[" + NeuroNetCom.stringVectorToString(client.getTargets(), ",") + "] ";
		retMess += " clientdataitems[" + NeuroNetCom.stringVectorToString(client.getDataItems(), ",") + "] ";

		retMess += " end:end";

		return retMess;
	    }
	    else
	    {
		NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:clientItem_is_null");
		return "status:error errorinfo:clientItem_is_null";
	    }
	}
	else
	{
	    NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:invalid_clientkey");
	    return "status:error errorinfo:invalid_clientkey";
	}
    }

    private String handleGetClients(String mess)
    {
	Vector<String> cli = NeuroNetServer.getClientList();

	NeuroNetServer.getGUI().addLogMessage(" getClientListe called ..");
	String retMess = "status:ok clients[";

	for (String client : cli)
	{
	    retMess += "" + client + ",";
	}
	// cut last ","
	if (cli.size() > 0)
	    retMess = retMess.substring(0, retMess.length() - 1);

	retMess += "] end:end";

	NeuroNetServer.getGUI().addLogMessage(" --" + retMess);
	return retMess;
    }

    private String handleGetTargets(String mess)
    {
	String clientID = NeuroNetCom.getParameterFormMessage("clientid", mess);
	String clientKey = NeuroNetCom.getParameterFormMessage("clientkey", mess);
	String requestClient = NeuroNetCom.getParameterFormMessage("requestclientid", mess);

	NeuroNetServer.getGUI().addLogMessage("Client " + clientID + " requests target info about " + requestClient);
	if (NeuroNetServer.checkAuth(clientID, clientKey))
	{
	    Vector<String> cli = NeuroNetServer.getTargetList(requestClient);

	    String retMess = "status:ok targets[";

	    for (String client : cli)
	    {

		retMess += "" + client + ",";
	    }
	    // cut last ","
	    if (cli.size() > 0)
		retMess = retMess.substring(0, retMess.length() - 1);

	    retMess += "] end:end";
	    NeuroNetServer.getGUI().addLogMessage(" --" + retMess);

	    return retMess;
	}
	else
	{
	    NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:invalid_clientkey");
	    return "status:error errorinfo:invalid_clientkey";
	}
    }

    private String handleGetMessages(String mess)
    {
	String clientID = NeuroNetCom.getParameterFormMessage("clientid", mess);
	String clientKey = NeuroNetCom.getParameterFormMessage("clientkey", mess);

	NeuroNetServer.getGUI().addLogMessage("Client " + clientID + " requests messages ");

	if (NeuroNetServer.checkAuth(clientID, clientKey))
	{
	    ClientsTableItem client = NeuroNetServer.getClientItem(clientID);
	    if (client != null)
	    {
		Vector<String> messages = client.getMessages();

		String retMess = "status:ok data[";
		NeuroNetServer.getGUI().addLogMessage(" --" + "status:ok");

		if (messages.size() > 0)
		{
		    // format data 
		    for (String message : messages)
		    {
			retMess += "[" + message + "],";
		    }
		    // cut last ","
		    retMess = retMess.substring(0, retMess.length() - 1);
		}
		retMess += "] end:end";

		return retMess;
	    }

	    else
	    {
		NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:client_not_registered");
		return "status:error errorinfo:client_not_registered";
	    }
	}
	else
	{
	    NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:invalid_clientkey");
	    return "status:error errorinfo:invalid_clientkey";
	}
    }

    private String handleResetMessage(String mess)
    {
	String clientID = NeuroNetCom.getParameterFormMessage("clientid", mess);
	String clientKey = NeuroNetCom.getParameterFormMessage("clientkey", mess);

	NeuroNetServer.getGUI().addLogMessage("Client " + clientID + " requests server reset ");

	if (NeuroNetServer.checkAuth(clientID, clientKey))
	{

	    String retMess = NeuroNetServer.reset();

	    NeuroNetServer.getGUI().addLogMessage(" --" + retMess);
	    return retMess;
	}
	else
	{
	    NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:invalid_clientkey");
	    return "status:error errorinfo:invalid_clientkey";
	}
    }

    /**
     * Method handels an logoff request from an auth client
     * @param mess
     * @return
     */
    private String handleLogoffMessage(String mess)
    {
	String clientID = NeuroNetCom.getParameterFormMessage("clientid", mess);
	String clientKey = NeuroNetCom.getParameterFormMessage("clientkey", mess);

	NeuroNetServer.getGUI().addLogMessage("Client " + clientID + " requests logoff ");

	if (NeuroNetServer.checkAuth(clientID, clientKey))
	{
	    String retMess =  NeuroNetServer.removeClient(clientID, clientKey);
	    NeuroNetServer.getGUI().addLogMessage(" --" + retMess);
	    return retMess;
	}
	else
	{
		NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:invalid_clientkey");
	    return "status:error errorinfo:invalid_clientkey";
	}

    }

    private String handleDataMessage(String mess)
    {
	String clientID = NeuroNetCom.getParameterFormMessage("clientid", mess);
	String clientKey = NeuroNetCom.getParameterFormMessage("clientkey", mess);
	
	NeuroNetServer.getGUI().addLogMessage("Client " + clientID + " sends data " );

	if (NeuroNetServer.checkAuth(clientID, clientKey))
	{

	    ClientsTableItem client = NeuroNetServer.getClientItem(clientID);
	    if (client != null)
	    {
		Vector<String> dataItems = client.getDataItems();
		Vector<String> dataTargets = client.getDataTargets();

		if (dataItems.size() == 0)
		    return "status:error errorinfo:no_dataItems_defined";
		if (dataTargets.size() == 0)
		    return "status:error errorinfo:no_dataTargets_defined";

		// collect data
		String data = NeuroNetCom.getParameterArrayFormMessage("data", mess);
		//	    for (String dataIt : dataItems)
		//	    {
		//		data += " " + dataIt + ":" + getParameterFormMessage(dataIt, mess);
		//	    }

		logger.info(" data is " + data);

		// check number of transmitted data
		if (dataItems.size() != data.split(",").length)
		{
		    NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:invalid_number_of_data_items");
		    return "status:error errorinfo:invalid_number_of_data_items";
		}

		// send data 
		for (String targetID : dataTargets)
		{
		    return NeuroNetServer.sendMessage(targetID, clientID, data);
		}
		String retMess = "status:ok";
		NeuroNetServer.getGUI().addLogMessage(" --" + retMess);
		return retMess;
	    }

	    else
	    {
		NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:client_not_registered");

		return "status:error errorinfo:client_not_registered";
	    }
	}
	else
	{
	    NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:invalid_clientkey");

	    return "status:error errorinfo:invalid_clientkey";
	}

    }

    private String handleConfigMessage(String mess)
    {
	String clientID = NeuroNetCom.getParameterFormMessage("clientid", mess);
	String clientKey = NeuroNetCom.getParameterFormMessage("clientkey", mess);

	String[] dataItems = NeuroNetCom.getParameterArrayFormMessageX("dataitems", mess);
	String[] dataTargets = NeuroNetCom.getParameterArrayFormMessageX("datatargets", mess);
	NeuroNetServer.getGUI().addLogMessage("Client " + clientID + " sets new configt ");

	if (NeuroNetServer.checkAuth(clientID, clientKey))
	{
	    ClientsTableItem client = NeuroNetServer.getClientItem(clientID);
	    if (client != null)
	    {
		// add dataItems
		if (dataItems != null && dataItems.length > 0)
		{
		    Vector<String> temp = new Vector<String>();
		    for (int i = 0; i < dataItems.length; i++)
		    {
			temp.add(dataItems[i]);
		    }
		    client.setDataItems(temp);
		}
		else
		{
			NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:no_dataItems_defined");
		    return "status:error errorinfo:no_dataItems_defined";
		}

		// add dataTargets
		if (dataTargets != null && dataTargets.length > 0)
		{
		    Vector<String> temp = new Vector<String>();
		    for (int i = 0; i < dataTargets.length; i++)
		    {
			temp.add(dataTargets[i]);
		    }
		    client.setDataTargets(temp);
		}
		else
		{
			NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:no_dataTargets_defined");
		    return "status:error errorinfo:no_dataTargets_defined";
		}

	    }
	    else
	    {
		NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:client_not_registered");

		return "status:error errorinfo:client_not_registered";
	    }
	    String retMess = "status:ok ";
		NeuroNetServer.getGUI().addLogMessage(" --" + retMess);
	    return retMess;
	}
	else
	{
	    NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:invalid_clientkey");

	    return "status:error errorinfo:invalid_clientkey";
	}
    }

    private String handleLogonMessage(String mess)
    {
	String clientID = NeuroNetCom.getParameterFormMessage("clientid", mess);
	String clientInfo = NeuroNetCom.getParameterFormMessage("clientinfo", mess);
	String clientKey = "-1";
	NeuroNetServer.getGUI().addLogMessage("Client " + clientID + " requests logon ");

	logger.info("client " + clientID + " requests join.");

	// check for minimal length
	if (clientID.length() < 1) {
	    NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:clientid_to_short");
	    return "status:error errorinfo:clientid_to_short";
	}
	// check for spaces
	if (clientID.contains(" ")){
	    NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:clientid_contains_spaces");
	    return "status:error errorinfo:clientid_contains_spaces";
	}
	boolean regClient = NeuroNetServer.isClientRegistered(clientID);
	logger.debug("isClientRegistered(clientID) returned " + regClient);
	if (!regClient)
	{
	    // add new client 
	    logger.info("adding client " + clientID + "");
	    ClientsTableItem newClient = new ClientsTableItem(clientID, clientInfo);
	    NeuroNetServer.addClientToTable(clientID, newClient);
	    clientKey = newClient.getClientKey();
	}
	else
	{
	    logger.error("adding client " + clientID + " faild, client id already exists");
		NeuroNetServer.getGUI().addLogMessage(" --" + "status:error errorinfo:client_alredy_logged_on_or_id_in_use");
	    return "status:error errorinfo:client_alredy_logged_on_or_id_in_use";
	}

	NeuroNetServer.getGUI().addLogMessage(" --" + "status:ok clientkey:" + clientKey);
	return "status:ok clientkey:" + clientKey;
    }

    //    public String sendToClient(String senderID, String data)
    //    {
    //	// TODO: DELETE (NOT USED, No Pushing of Messages)
    //	String sendString = "incomingtime:" + System.nanoTime() + " senderid:" + senderID + " " + "data[" + data + "]";
    //
    //	try
    //	{
    //	    outputStream.writeObject(sendString);
    //	    outputStream.flush();
    //
    //	}
    //	catch (IOException e)
    //	{
    //	    logger.error("status:error errorinfo:IOError_while_sending", e);
    //	    return "status:error errorinfo:IOError_while_sending";
    //	}
    //
    //	return "status:ok ";
    //
    //    }
}
